import { EventHandler, Nodes, dom } from '@knno/dom';
import { fieldStyle } from './field.css';
import { type InputType } from '../input/input';
import { FormItem } from '../interface';
import { Toggle, toggle } from '../toggle/toggle';
import { DATA } from '../symbol';
import SVGEdite from '@material-design-icons/svg/filled/edit.svg';
import SVGArrowRight from '../../svg/right-arrow.svg';

export type FieldType = 'input' | 'readonly' | 'entry' | 'subject' | 'subject-entry' | 'toggle';

export interface FieldEventMap {
	checked: Event;
}
export type FieldEventKey = keyof FieldEventMap;
export type FieldEventHandler<T extends FieldEventKey, O extends Nodes> = (this: O, event: FieldEventMap[T]) => void;

export interface Field {
	on<K extends keyof FieldEventMap>(
		event: K,
		listener: FieldEventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	on<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof FieldEventMap>(
		event: K,
		listener: FieldEventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
}

export class Field extends Nodes implements FormItem {
	private [DATA]: {
		type: FieldType;
		label?: string;
		value?: string;
		placeholder?: string;
		icon?: Nodes;
		inputType: InputType;
		maxLength?: number;
		disabled: boolean;
		checked: boolean;
		toggle?: Toggle;
	};

	constructor() {
		super(
			dom.div(fieldStyle).on('contextmenu', (evt) => {
				evt.preventDefault();
			})
		);
		this[DATA] = { type: 'readonly', inputType: 'text', disabled: false, checked: false };
		this.render();
	}

	private render() {
		const d = this[DATA];
		d.toggle = undefined;
		this.html('').attr('type', d.type);
		if (d.type === 'readonly' || d.type === 'entry' || d.type === 'toggle') {
			this.append(
				dom
					.div()
					.name('label')
					.append(d.icon ?? [], dom.label().text(d.label ?? '')),
				dom
					.div()
					.name('value')
					.text(d.value ?? '')
			);
			if (d.type === 'entry') {
				this.append(dom.html(SVGArrowRight).name('arrow'));
			} else if (d.type === 'toggle') {
				d.toggle = toggle();
				d.toggle.disabled(d.disabled);
				d.toggle.on('click', () => {
					this.emit(new Event('checked'));
				});
				this.append(d.toggle);
			}
		} else if (d.type === 'input') {
			const input = dom.input().text(d.value ?? '');
			input.on('input', (evt) => {
				this.emit(
					new InputEvent('input', { data: (evt as InputEvent).data, inputType: (evt as InputEvent).inputType })
				);
			});
			input.on('change', () => {
				this.emit(new Event('change'));
			});
			input.attr('disabled', d.disabled);
			input.attr('type', d.inputType);
			input.attr('placeholder', d.placeholder ?? '');
			input.attr('maxlength', d.maxLength ? d.maxLength + '' : null);
			this.append(
				dom
					.div()
					.name('label')
					.append(d.icon ?? [], dom.label().text(d.label ?? '')),
				input,
				dom.html(SVGEdite).name('arrow')
			);
		} else {
			this.append(
				d.icon ?? [],
				dom
					.div()
					.name('box')
					.append(
						dom.label().text(d.label ?? ''),
						dom
							.div()
							.name('value')
							.text(d.value ?? '')
					)
			);
			if (d.type === 'subject-entry') {
				this.append(dom.html(SVGArrowRight).name('arrow'));
			}
		}
	}

	disabled(): boolean;
	disabled(value: boolean): this;
	disabled(value?: boolean): boolean | this {
		if (value === undefined) {
			return this[DATA].disabled;
		} else {
			this[DATA].disabled = value;
			this[DATA].toggle?.disabled(value);
			this.query('input').attr('disabled', value);
			return this;
		}
	}

	checked(): boolean;
	checked(value: boolean): this;
	checked(value?: boolean): boolean | this {
		if (value === undefined) {
			return this[DATA].checked;
		} else {
			this[DATA].checked = value;
			this[DATA].toggle?.checked(value);
			return this;
		}
	}

	inputType(): InputType;
	inputType(type: InputType): this;
	inputType(type?: InputType): this | InputType {
		if (type === undefined) {
			return this[DATA].inputType;
		}
		this[DATA].inputType = type;
		this.query('input').attr('type', type);
		return this;
	}

	type(): FieldType;
	type(type: FieldType): this;
	type(type?: FieldType): FieldType | this {
		if (type === undefined) {
			return this[DATA].type;
		}
		this[DATA].type = type;
		this.render();
		return this;
	}

	icon(icon: string | null): this {
		if (icon) {
			this[DATA].icon = dom.html(icon).name('icon');
		} else {
			this[DATA].icon = undefined;
		}
		this.render();
		return this;
	}

	label(): string;
	label(text: string): this;
	label(text?: string): this | string {
		if (text === undefined) {
			return this[DATA].label ?? '';
		} else {
			this[DATA].label = text;
			this.query('label').text(text);
			return this;
		}
	}

	override text(): string;
	override text(text: string): this;
	override text(text?: string): this | string {
		if (text === undefined) {
			return this[DATA].value ?? '';
		} else {
			this[DATA].value = text;
			this.query('div[name=value]').text(text);
			this.query('input').prop('value', text);
			return this;
		}
	}

	override value(): string;
	override value(text: string): this;
	override value(text?: string): this | string {
		if (text) return this.text(text);
		else return this.text();
	}

	override focus(options?: FocusOptions | undefined): this {
		const input = this.query('input');
		input.focus(options);
		return this;
	}

	placeholder(): string;
	placeholder(text: string): this;
	placeholder(text?: string): this | string | undefined {
		if (text === undefined) {
			return this[DATA].placeholder;
		}
		this[DATA].placeholder = text;
		this.query('input').prop('placeholder', text);
		return this;
	}

	maxLength(): number | null;
	maxLength(len: number | null): this;
	maxLength(len?: number | null): this | number | null {
		if (len === undefined) {
			return this[DATA].maxLength ?? null;
		}
		this[DATA].maxLength = len ?? undefined;
		this.query('input').attr('maxlength', len ? len + '' : null);
		return this;
	}
}

export function field(...className: string[]): Nodes {
	return new Field().addClass(...className);
}
